/*
  创建一个Promise
  1. Promise 就是一个类 在执行这个类的时候 需要传递一个执行器进去 执行器会立即执行
  2. Promise 中有三种状态 分别为 成功 fulfilled 失败 rejected 等待 pending
  pending => fulfilled
  penging => rejected
  3. resolve和reject函数是用来更改状态的
    resolve: fulfilled
    rejected: rejected
  4. then方法内部做的事情就是判断状态 如果状态是成功，就调用成功的方法，如果状态是失败，就调用失败的方法，
    每个promise对象都能调用then方法，所以then方法应该是定义在原型对象上的
  5. then成功回调有一个参数 表成功之后的值 then失败回调也有一个参数 表失败后的原因
    */
// let promise = new Promise((resolve, reject) => {
//   // 传递给resolve和reject的参数，就是成功之后的值，失败的原因
//   resolve('success') // success就是 成功之后的值
//   reject('fail')  // fail就是 失败的原因
// })
// promise.then(() => {}, () => {})
// promise.then(value => {}, err => {})
/* 
  1. 通过class关键字创建一个类
  2. 传递一个执行器，通过构造函数construtor接收这个执行器 executor
  3. 执行器executor立即执行，其实就是回调函数，所以就调用executor
  4. 调用执行器的时候，传入resolve，reject
  5. 定义resolve和reject属性，使用箭头函数
  6. 调用resolve，reject，会更改状态，需要定义状态属性
  7. 状态是每个Promise所独有的，需要定义实例属性，默认为等待pending
  8. then方法内部做的事情就是判断状态 如果状态是成功，就调用成功的方法，如果状态是失败，就调用失败的方法，
    每个promise对象都能调用then方法，所以then方法应该是定义在原型对象上的
  9. then成功回调有一个successCallback函数参数 表成功之后的值 then失败回调也有一个failCallback函数参数 表失败后的原因
  10. 在状态为resovle时会传入一个value值，同样在状态为reject的时候也会传入一个reason，因为会被上面的成功和失败回调中调用，所以需要定义实例属性进行存储
  11. ---处理异步的情况--这里其实没有对异步操作进行处理，异步操作就代表Promise调用then时，获取不到状态，即走不到resolve或者是reject，
    而是一直处于等待状态pending，所以在then方法的时候，其实还要对等待pending的状态做处理
  12. 将成功和失败的回调存储起来，添加成功回调和失败回调的实例属性
  13. 当状态处于等待状态时，在相应的成功，失败状态里判断是否有相应的成功，失败的回调，如果有就执行，就能实现异步处理的情况
  14. --处理多个回调的情况--以数组的形式存储多个回调，然后再对数组进行遍历调用回调函数
  15. --then--的链式调用，then方法是在Promise里面的，而then方法可以链式调用，所以then方法返回的应该也是一个Promise
  16. --如何让then方法里面的返回值，传递给下一个then方法--先拿到回调函数的返回值
  17. --判断then返回的值，是普通值还是Promise对象--
  18. --then返回的对象是当前的Promise--，就会出现promise自己调用自己，然后就会出现错误
  19. 捕获错误，错误异常处理 加 try catch
    a. 第一次要处理的是执行器，当执行器执行错误，要把状态改成失败，在then的第二个参数获取错误
    b. then方法里面的回调函数，当then方法里面的回调函数执行出错，那么要在下一个then方法的第二参数里捕获
  20. then链式调用其他状态的补充，如失败reject，异步代码的错误异常处理
  21. all的实现，用来解决异步并发问题，允许用异步调用的顺序，得到异步代码执行的结果
    a. Promise.all接收一个数组作为参数，数组内可以是普通值，也可以是promise对象，数组里值的顺序，一定是得到结果的顺序
    b. Promise.all的返回值也是一个Promise对象，可以使用then进行链式调用
    c. Promise.all特点，所有promise对象如果状态都是成功的，那么就是成功的，只要有一个失败就会返回失败
    d. 调用方式：类调用all，所以肯定是静态方法
  22. Promise的resolve方法，作用是将给定的值转换成promise对象，即返回一个Promise对象，这个promise对象会包裹给定的值
    a. 判断给定的值是不是promise对象，如果是原封不动的返回，如果不是就创建一个promise，把值包裹在promise里
  23. Promise的finally方法
    a. 无论promise返回的成功还是失败，finally都会被执行一次
    b. finally方法后面可以链式调用then方法获取返回值
  24. Promise的catch方法 作用用来处理这个promise对象最终失败的情况，then方法是可以不传递失败方法的，直接在catch里处理
*/
const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected' // 失败

class MyPromise {
  constructor(executor) {
    // 捕获执行器的错误
    try {
      // 在类的里面，通过this进行访问，所以改为this.resolve
      // executor(resolve, reject)
      executor(this.resolve, this.reject)
    } catch (e) {
      // 错误就调用reject方法
      this.reject(e)
    }
  }
  // 定义属性，promise状态
  status = PENDING

  // 成功之后的值
  value = undefined
  // 失败之后的值
  reason = undefined
  // 成功回调
  successCallback = []
  // 失败回调
  failCallback = []
  // 使用箭头函数的原因，为了让函数内部的this指向，指向这个类的实例对象，也就是Promise对象
  resolve = value => { // 成功后传入的值 value
  // resolve = () => {
    // 如果状态不是等待 阻止程序向下执行
    if (this.status !== PENDING) return
    // 将状态更改为成功
    this.status = FULFILLED
    // 保存成功之后的值
    this.value = value
    // 判断成功回调是否存在，如果存在就调用
    // this.successCallback && this.successCallback(this.value)
    // 当数组长度不等于0，有回调函数，进行调用
    while(this.successCallback.length) {
      // 从前往后进行调用，直接取出最前面的回调函数，并且调用，把成功的值传入
      this.successCallback.shift()()
    }
  }
  reject = reason => { // 失败后传入的值 reason
  // reject = () => {
    // 如果状态不是等待 阻止程序向下执行
    if (this.status !== PENDING) return
    // 将状态更改为失败
    this.status = REJECTED
    // 保存失败之后的值
    this.reason = reason
    // 判断失败回调是否存在，如果存在就调用
    // this.failCallback && this.failCallback(this.reason)
    // 当数组长度不等于0，有回调函数，进行调用
    while(this.failCallback.length) {
      // 从前往后进行调用，直接取出最前面的回调函数，并且调用，把失败的值传入
      this.failCallback.shift()()
    }
  }
  then(successCallback, failCallback) {
    // 参数可选
    successCallback = successCallback ? successCallback : value => value;
    // 参数可选
    failCallback = failCallback ? failCallback: reason => { throw reason };
    // promise里面传入的是一个立即执行器，就是里面的代码会立即执行
    let promise2 = new MyPromise((resolve, reject) => {
      // 判断状态
      if (this.status === FULFILLED) {
        setTimeout(() => {
          // then方法里面的回调函数的异常错误处理
          try {
            // 传入成功之后的值
            // x 就是当前回调函数的返回值
            let x = successCallback(this.value)
            // 判断 x 的值是普通值还是promise对象
            // **如果是普通值，直接调用resolve**
            // **如果是promise对象 查看promise对象返回的结果**
            // **再根据promise对象返回的结果，决定调用resolve 还是调用reject**
            // resolvePromise(x, resolve, reject)
            // 判断promise2和成功的回调返回的promise，如果相等的话，就是自己返回自己
            // 把代码变成异步代码，让同步代码全部执行完毕
            resolvePromise(promise2, x, resolve, reject) // 这里的promise2实际是取不到的，因为还没有执行new Promise
          } catch(e) {
            reject(e)
          }
        }, 0)
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          // then方法里面的回调函数的异常错误处理
          try {
            // 传入失败之后的值
            // x 就是当前回调函数的返回值
            let x = failCallback(this.reason)
            // 判断 x 的值是普通值还是promise对象
            // **如果是普通值，直接调用resolve**
            // **如果是promise对象 查看promise对象返回的结果**
            // **再根据promise对象返回的结果，决定调用resolve 还是调用reject**
            // resolvePromise(x, resolve, reject)
            // 判断promise2和成功的回调返回的promise，如果相等的话，就是自己返回自己
            // 把代码变成异步代码，让同步代码全部执行完毕
            resolvePromise(promise2, x, resolve, reject) // 这里的promise2实际是取不到的，因为还没有执行new Promise
          } catch(e) {
            reject(e)
          }
        }, 0)
      } else {
        // 等待
        // 将成功和失败的回调存储起来，以数组的形式存储多个回调函数
        this.successCallback.push(() => {
          setTimeout(() => {
            // then方法里面的回调函数的异常错误处理
            try {
              // 传入成功之后的值
              // x 就是当前回调函数的返回值
              let x = successCallback(this.value)
              // 判断 x 的值是普通值还是promise对象
              // **如果是普通值，直接调用resolve**
              // **如果是promise对象 查看promise对象返回的结果**
              // **再根据promise对象返回的结果，决定调用resolve 还是调用reject**
              // resolvePromise(x, resolve, reject)
              // 判断promise2和成功的回调返回的promise，如果相等的话，就是自己返回自己
              // 把代码变成异步代码，让同步代码全部执行完毕
              resolvePromise(promise2, x, resolve, reject) // 这里的promise2实际是取不到的，因为还没有执行new Promise
            } catch(e) {
              reject(e)
            }
          }, 0)
        })
        this.failCallback.push(() => {
          setTimeout(() => {
            // then方法里面的回调函数的异常错误处理
            try {
              // 传入失败之后的值
              // x 就是当前回调函数的返回值
              let x = failCallback(this.reason)
              // 判断 x 的值是普通值还是promise对象
              // **如果是普通值，直接调用resolve**
              // **如果是promise对象 查看promise对象返回的结果**
              // **再根据promise对象返回的结果，决定调用resolve 还是调用reject**
              // resolvePromise(x, resolve, reject)
              // 判断promise2和成功的回调返回的promise，如果相等的话，就是自己返回自己
              // 把代码变成异步代码，让同步代码全部执行完毕
              resolvePromise(promise2, x, resolve, reject) // 这里的promise2实际是取不到的，因为还没有执行new Promise
            } catch(e) {
              reject(e)
            }
          }, 0)
        })
        // this.successCallback.push(successCallback)
        // this.failCallback.push(failCallback)
        // this.successCallback = successCallback
        // this.failCallback = failCallback
      }
    })
    // 返回一个Promise，以实现then的链式调用
    return promise2
  }
  // 接收一个回调函数，不管promise状态失败还是成功都会执行这个回调函数
  finally(callback) {
    // 获取当前promise的状态
    // 在成功和失败里都要调用回调方法
    // finally后面要链式调用then方法，所以finally就要返回一个promise，then方法返回的就是promise对象，直接return then方法即可
    return this.then(value => {
      // 等待异步代码执行完毕
      return MyPromise.resolve(callback()).then(() => value)
    }, reason => {
      return MyPromise.resolve(callback()).then(() => { throw reason })
    })
  }
  // 接收一个参数
  catch(failCallback) {
    // 调用then方法，注册failCallback，then第一个参数是成功回调，传入一个undefined
    return this.then(undefined, failCallback)
  }
  // 静态方法
  // 传入的形参是一个数组
  static all (array) {
    // 定义一个结果数组
    let result = []
    // 解决异步代码是否处理完，
    let index = 0
    // 判断数组内的参数是普通值还是promise
    return new MyPromise((resolve, reject) => {
      // 定义一个方法，把值放到数组里
      // 每次调用addData，index++，判断index的值和array的长度是否相等，如果相等就可以调用resolve
      function addData(key, value) {
        result[key] = value
        index++
        if(index === array.length) {
          resolve(result)
        }
      }
      // 循环遍历判断，for循环是立即执行的，是没有对异步代码进行处理的，会导致结果出现空值
      for(let i = 0; i < array.length; i++) {
        let current = array[i]
        // 判断current的普通值还是promise对象
        if (current instanceof MyPromise) {
          // promise对象，只要有一个失败就会直接reject
          current.then(value => addData(i, value), reason => reject(reason))
        } else {
          // 普通值直接放到result里
          addData(i, array[i])
        }
      }
    })
  }
  static resolve (value) {
    if(value instanceof MyPromise) return value
    return new MyPromise(resolve => resolve(value))
  }
  
}
function resolvePromise(promise2, x, resolve, reject) {
  // 判断promise是否返回自己本身，判断返回promise和成功回调返回的promise对象
  if (promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  // 判断x值的普通值还是一个Promise对象
  if (x instanceof MyPromise) { // 判断 x是不是MyPromise的实例对象
    // promise对象
    // x.then(value => resolve(value), reason => reject(reason))
    x.then(resolve, reject)
  } else {
    // 普通值
    resolve(x)
  }
}

module.exports = MyPromise